How to use AutoMapper in .NET
TLDR
- AutoMapper is used to simplify the tedious property assignment between DTOs and Entities in layered architectures.
MapperConfigurationshould be maintained as a singleton; it is recommended to complete the configuration during application startup (e.g.,Program.cs).- Always use
config.AssertConfigurationIsValid()to verify mapping rules and ensure that all members of the Destination have corresponding sources. - Use the
Profileclass to modularize mapping logic and improve code maintainability. - For complex logic, you can use
ForMemberto perform conditional mapping, ignore fields, or define custom conversion rules. - Use
ReverseMap()to simplify bidirectional mapping, though complex conversions require additionalForPathdefinitions for reverse logic. - It is recommended to use the
AutoMapper.Extensions.Microsoft.DependencyInjectionpackage to automatically register mapping configurations via the DI container.
Core Usage of AutoMapper
In layered architectures, to achieve separation of concerns, conversions between Entities, Service DTOs, and ViewModels are often required. AutoMapper simplifies these property assignment operations through Reflection.
Basic Configuration Workflow
When to encounter this issue: When you need to initialize mapping rules and ensure their correctness.
// Create a MapperConfiguration to define the mapping relationship between classes
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Order, OrderDto>();
});
// Verify the Configuration settings; if any member in the Destination is not mapped, an AutoMapperConfigurationException will be thrown
config.AssertConfigurationIsValid();
// Create the Mapper
var mapper = config.CreateMapper();
// Map the source values to a new Destination object
Destination dest = mapper.Map<Destination>(source);
// Map the source values to an existing dest object
mapper.Map(source, dest);Mapping Rule Configuration
Common Mapping and Profile Organization
When to encounter this issue: When a project has multiple classes that need to be mapped and you want to manage the configurations centrally.
By inheriting from the Profile class, you can split mapping logic and reference it via AddProfile:
public class OtherProfile : Profile {
public OtherProfile() {
// Configure that Source can be converted to Destination
CreateMap<Source, Destination>();
// Use ConvertUsing to define global conversion logic (e.g., string trimming)
CreateMap<string?, string?>().ConvertUsing(x => x == null ? x : x.Trim());
}
}Handling Property Prefixes/Postfixes
When to encounter this issue: When the naming conventions of properties in Source and Destination are inconsistent (e.g., containing Prefixes or Postfixes).
var config = new MapperConfiguration(cfg => {
cfg.RecognizePrefixes("Prefix");
cfg.RecognizePostfixes("Postfix");
cfg.RecognizeDestinationPrefixes("Prefix");
cfg.RecognizeDestinationPostfixes("Postfix");
// If you do not want to use the default "Get" prefix
cfg.ClearPrefixes();
});Advanced Configuration for a Single Member
When to encounter this issue: When you need to handle special logic, such as ignoring fields, conditional mapping, or default value handling.
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Source, Destination>()
.ForMember(desc => desc.Prop1, opt => opt.Ignore()) // Ignore specific fields
.ForMember(dest => dest.DestProp2, opt => opt.MapFrom(src => src.SourceProp2)) // Specify corresponding field
.ForMember(desc => desc.DateProp3, opt => opt.MapFrom(src => DateTime.Now)) // Fixed or calculated value
.ForMember(dest => dest.IntProp4, opt => opt.Condition(src => (src.IntProp4 >= 0))) // Conditional mapping
.ForMember(dest => dest.Prop5, opt => opt.NullSubstitute("Other Value")); // Null substitute value
});Reverse Mapping (ReverseMap)
When to encounter this issue: When you need to implement bidirectional conversion and do not want to write CreateMap twice.
Use ReverseMap() to quickly create reverse rules. Note that complex conversions require ForPath to define reverse logic, and AssertConfigurationIsValid() does not apply to reverse mappings.
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Source, Destination>()
.ForMember(dest => dest.Prop5, opt => opt.MapFrom(src => src.Prop3 + "," + src.Prop4))
.ReverseMap()
.ForPath(s => s.Prop3, opt => opt.MapFrom(src => src.Prop5.Split(new char[] { ',' })[0]));
});Dependency Injection Integration
When to encounter this issue: In .NET Core or .NET 6+ environments, when you want to automatically manage the Mapper lifecycle via a DI container.
You need to install the AutoMapper.Extensions.Microsoft.DependencyInjection package.
// Register in Program.cs or Startup.cs
builder.Services.AddAutoMapper(typeof(ProfileTypeFromAssembly1), typeof(ProfileTypeFromAssembly2));
// Inject and use in Controller or Service
public class EmployeesController {
private readonly IMapper mapper;
public EmployeesController(IMapper mapper) => this.mapper = mapper;
}Change Log
- 2022-10-24 Initial document created.